YAML 模板库
本篇收集常用 Kubernetes 资源的 YAML 清单模板,每份都带中文注释、可直接 kubectl apply 使用。模板遵循基本最佳实践:镜像固定版本号、配好 labels、配好 resources、配好探针。复制后改改名称、镜像和域名即可上手。
手写清单容易漏字段,更稳妥的方式是先让 kubectl 生成骨架,再按需补充:
# 只渲染不提交,输出到文件后再编辑
kubectl create deployment my-app --image=nginx:1.27 --dry-run=client -o yaml > deploy.yaml
# 其他常用资源同理
kubectl create service clusterip my-svc --tcp=80:80 --dry-run=client -o yaml
kubectl create configmap my-config --from-literal=key=value --dry-run=client -o yaml
kubectl create job my-job --image=busybox:1.36 --dry-run=client -o yaml
--dry-run=client 表示只在本地渲染、不真正提交到集群。注意不是所有资源都支持 kubectl create(比如 Ingress、StatefulSet 就不行),这类只能手写或从官方文档复制。
Pod
适用场景:一般不建议单独创建 Pod(挂了没人重建),多用于调试、一次性验证等临时场景。这份模板包含探针与资源限制,也是理解 Pod spec 的最小完整示例。
apiVersion: v1
kind: Pod
metadata:
name: my-app-pod # Pod 名称
namespace: default # 所属命名空间
labels: # 标签,供 Service/Deployment 的选择器使用
app: my-app
version: v1
spec:
containers:
- name: my-app # 容器名称
image: nginx:1.27 # 固定版本号,禁止用 latest(无法保证副本间版本一致)
imagePullPolicy: IfNotPresent # 节点已有镜像就不拉取,加快启动
ports:
- containerPort: 80 # 声明容器监听端口(信息性质,不做网络限制)
env: # 环境变量
- name: TZ
value: Asia/Shanghai
resources: # 资源配额:生产环境必须配置
requests: # 调度依据:保证至少分到这么多
cpu: 100m # 0.1 核
memory: 128Mi
limits: # 使用上限:CPU 超限被限流,内存超限被杀死
cpu: 500m
memory: 256Mi
livenessProbe: # 存活探针:失败会重启容器
httpGet:
path: / # 健康检查路径
port: 80
initialDelaySeconds: 10 # 容器启动后等 10 秒再开始探测
periodSeconds: 15 # 每 15 秒探测一次
failureThreshold: 3 # 连续失败 3 次才判定不健康
readinessProbe: # 就绪探针:未通过则从 Service 摘除流量
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
restartPolicy: Always # 重启策略:Always / OnFailure / Never
使用要点:
livenessProbe失败会重启容器,readinessProbe失败只是摘除流量,两者检测的接口可以不同。- 探针不要用太重的接口(比如查库全表),高峰期容易误判,引发连环重启。
- Pod 里可以配多个容器(Sidecar 模式),彼此通过 localhost 通信、共享存储卷。
Deployment
适用场景:无状态应用的标准部署方式,管理多个 Pod 副本并支持滚动更新与回滚。配套 PodDisruptionBudget,保证驱逐节点等主动运维操作时的可用性。
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-app
namespace: default
labels:
app: my-app
spec:
replicas: 3 # 期望副本数
revisionHistoryLimit: 5 # 保留的历史版本数,供回滚使用
selector:
matchLabels: # 必须与 template.metadata.labels 一致
app: my-app
strategy: # 更新策略
type: RollingUpdate # 滚动更新(另一种是 Recreate:先全停再全起)
rollingUpdate:
maxSurge: 1 # 更新时最多多创建 1 个 Pod(也可写百分比,如 25%)
maxUnavailable: 0 # 更新时最多允许 0 个不可用,保证服务不中断
template: # Pod 模板
metadata:
labels:
app: my-app
spec:
containers:
- name: my-app
image: nginx:1.27
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 15
---
# PDB:限制主动运维操作(如驱逐节点)时最多允许多少 Pod 不可用
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: my-app-pdb
namespace: default
spec:
minAvailable: 2 # 任何时刻至少保证 2 个 Pod 可用(也可改用 maxUnavailable: 1)
selector:
matchLabels:
app: my-app
使用要点:
maxSurge: 1加maxUnavailable: 0是求稳的常见组合:先多拉起一个新 Pod,就绪后再下掉旧的。- PDB 只约束主动驱逐(如
kubectl drain),管不了节点宕机、OOM 这类意外,副本数本身要留冗余。 - 回滚用
kubectl rollout undo deployment/my-app,可回退的版本数由revisionHistoryLimit控制。
Service 与 Ingress
ClusterIP 适用场景:集群内部服务间通信,是 Service 的默认类型。
apiVersion: v1
kind: Service
metadata:
name: my-app-svc
namespace: default
labels:
app: my-app
spec:
type: ClusterIP # 分配集群内部 IP,仅集群内可访问
selector: # 按标签选中后端 Pod
app: my-app
ports:
- name: http
port: 80 # Service 暴露的端口
targetPort: 80 # 转发到容器的端口(对应 containerPort)
protocol: TCP
NodePort 适用场景:没有负载均衡器时临时对外暴露服务,或配合四层负载使用。
apiVersion: v1
kind: Service
metadata:
name: my-app-nodeport
namespace: default
spec:
type: NodePort # 在每台节点上开一个高位端口
selector:
app: my-app
ports:
- port: 80
targetPort: 80
nodePort: 30080 # 节点上监听的端口,范围 30000-32767;不写则随机分配
Ingress 适用场景:七层 HTTP/HTTPS 统一入口,按域名和路径路由,配合 cert-manager 可自动签发证书。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
namespace: default
annotations:
# 注解按实际 Ingress Controller 调整,这里以 ingress-nginx 为例
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx # 指定由哪个 Ingress Controller 处理
tls: # HTTPS 配置
- hosts:
- app.example.com # 证书覆盖的域名
secretName: my-app-tls # 存放证书的 Secret(内含 tls.crt 和 tls.key)
rules:
- host: app.example.com # 匹配的域名
http:
paths:
- path: / # 匹配路径
pathType: Prefix # Prefix 前缀匹配 / Exact 精确匹配
backend:
service:
name: my-app-svc
port:
number: 80 # 后端 Service 的端口
使用要点:
- Service 的
port是它自己暴露的端口,targetPort才是容器端口,两者不一致是常见踩坑点。 - TLS Secret 用
kubectl create secret tls my-app-tls --cert=证书文件 --key=私钥文件创建,或交给 cert-manager 自动管理。 - Ingress 只是路由规则,集群里必须有 Ingress Controller(ingress-nginx、traefik 等)运行才会生效。
ConfigMap 与 Secret
适用场景:把配置和密钥从镜像里剥离出来,同一份镜像在不同环境挂不同配置。Secret 用 stringData 可以直接写明文,提交时自动转 base64。
apiVersion: v1
kind: ConfigMap
metadata:
name: my-app-config
namespace: default
labels:
app: my-app
data: # 普通配置,键值对形式
APP_ENV: "production" # 值必须是字符串,数字和布尔要加引号
LOG_LEVEL: "info"
app.conf: | # 也可以存整段配置文件,竖线表示保留换行
server {
listen 80;
server_name example.com;
}
---
apiVersion: v1
kind: Secret
metadata:
name: my-app-secret
namespace: default
type: Opaque # 通用类型;还有 tls、dockerconfigjson 等专用类型
stringData: # 明文书写,kubectl 提交时自动 base64 编码
DB_PASSWORD: "s3cureP@ss"
api-token: "abcd1234"
使用要点:
- Secret 只是 base64 编码而非加密,真正的安全靠 RBAC 限制访问、开启 etcd 静态加密;含密钥的清单不要提交进 Git。
- 以环境变量注入的 ConfigMap/Secret 更新后不会自动生效,需重启 Pod;以 volume 挂载的会自动更新(有几十秒延迟)。
stringData是写时专用字段,kubectl get回读时看到的是 base64 后的data。
Job 与 CronJob
适用场景:Job 跑一次性任务(数据迁移、批处理),CronJob 按 cron 表达式定时跑(备份、报表)。
apiVersion: batch/v1
kind: Job
metadata:
name: db-migrate
namespace: default
spec:
completions: 1 # 需要成功完成的次数
parallelism: 1 # 同时并行的 Pod 数
backoffLimit: 3 # 失败重试上限,超过则标记 Job 失败
activeDeadlineSeconds: 600 # 整个 Job 的最长运行时间,超时强制终止
ttlSecondsAfterFinished: 86400 # 完成后保留 1 天自动清理,避免堆积
template:
spec:
restartPolicy: Never # Job 里只能是 Never 或 OnFailure,不能 Always
containers:
- name: migrate
image: my-registry/migrate:1.4.2
command: ["./migrate", "up"] # 覆盖镜像默认入口
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: "1"
memory: 512Mi
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: db-backup
namespace: default
spec:
schedule: "0 3 * * *" # cron 表达式:每天凌晨 3 点
timeZone: "Asia/Shanghai" # 调度时区(Kubernetes 1.27+ 稳定);不支持时按 UTC 换算
concurrencyPolicy: Forbid # 上次没跑完怎么办:Forbid 跳过 / Replace 替换 / Allow 并行
successfulJobsHistoryLimit: 3 # 保留的成功 Job 记录数
failedJobsHistoryLimit: 1 # 保留的失败 Job 记录数
startingDeadlineSeconds: 300 # 错过调度时间 300 秒内允许补跑
jobTemplate: # Job 模板,字段同上面的 Job
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: my-registry/backup:2.1.0
command: ["/bin/sh", "-c", "./backup.sh"]
resources:
requests:
cpu: 200m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
使用要点:
- 旧版本集群没有
timeZone字段时,schedule按控制器所在时区(通常 UTC)计算,想按北京时间执行要自己换算。 - 任务耗时可能超过调度间隔时,一定配
concurrencyPolicy: Forbid,避免任务叠加。 - 手动从 CronJob 触发一次任务:
kubectl create job --from=cronjob/db-backup db-backup-manual。
StatefulSet
适用场景:有状态应用(数据库、消息队列),需要稳定的网络标识(固定 Pod 名)和各自独立的持久存储,必须搭配 headless Service 使用。
# Headless Service:clusterIP 为 None,不分配虚拟 IP,
# 作用是给每个 Pod 提供固定 DNS 记录(Pod名.Service名.命名空间.svc.cluster.local)
apiVersion: v1
kind: Service
metadata:
name: redis-headless
namespace: default
labels:
app: redis
spec:
clusterIP: None # None 即 headless,StatefulSet 的标配
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis
namespace: default
spec:
serviceName: redis-headless # 必填,关联上面的 headless Service
replicas: 3 # Pod 名固定为 redis-0、redis-1、redis-2
podManagementPolicy: OrderedReady # 默认按顺序逐个创建/删除;Parallel 则并行
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.2
ports:
- containerPort: 6379
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
volumeMounts:
- name: data # 对应下面 volumeClaimTemplates 的名称
mountPath: /data
volumeClaimTemplates: # 为每个 Pod 独立创建 PVC,Pod 删除后 PVC 默认保留
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"] # 单节点读写
storageClassName: local-path # 按集群实际可用的 StorageClass 修改
resources:
requests:
storage: 10Gi # 每个 Pod 独立 10Gi 存储
使用要点:
- Pod 按序号滚动创建(先起 0 再起 1),主从架构里通常 0 号是主节点;缩容永远从最大序号开始删。
- 删除 StatefulSet 不会删除 PVC,数据盘要手动清理,这是防止误删数据的保护机制。
- 有状态应用别指望 K8s 自动做主从切换、数据同步,这些要靠应用自身或 Operator 解决。
RBAC
适用场景:给应用(而不是人)授予最小权限,比如 Pod 内的程序需要读 ConfigMap、列出 Pod。ServiceAccount、Role、RoleBinding 三件套配合使用。
# ServiceAccount:Pod 在集群里的身份
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-app-sa
namespace: default
---
# Role:定义「能干什么」,只在当前命名空间内生效
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: configmap-reader
namespace: default
rules:
- apiGroups: [""] # 核心 API 组(v1 资源)写空字符串;apps 组则写 "apps"
resources: ["configmaps"] # 资源名,用复数形式
verbs: ["get", "list", "watch"] # 允许的操作
- apiGroups: ["apps"]
resources: ["deployments"]
verbs: ["get", "list"]
---
# RoleBinding:把 Role 绑定到 ServiceAccount,权限才真正生效
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: my-app-read-config
namespace: default
subjects: # 授权对象,可以写多个
- kind: ServiceAccount
name: my-app-sa
namespace: default
roleRef: # 引用的角色
kind: Role
name: configmap-reader
apiGroup: rbac.authorization.k8s.io
使用要点:
- Pod 里通过
spec.serviceAccountName: my-app-sa使用这个身份,不指定则用命名空间的 default SA。 - Role/RoleBinding 只在命名空间内生效;跨命名空间权限用 ClusterRole/ClusterRoleBinding,授权要更谨慎。
- 遵循最小权限原则:只给应用真正需要的资源和操作,图省事直接给 cluster-admin 是安全事故的常见来源。
ResourceQuota 与 LimitRange
适用场景:多团队共用集群时,按命名空间限制资源总量(ResourceQuota)和单个容器的默认值与上下限(LimitRange),防止某个团队把集群资源吃光。
# ResourceQuota:限制整个命名空间的资源总量
apiVersion: v1
kind: ResourceQuota
metadata:
name: team-quota
namespace: team-a
spec:
hard: # 硬上限,超出则拒绝创建新资源
requests.cpu: "10" # 所有 Pod 的 requests.cpu 总和上限
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50" # 最多 50 个 Pod
services: "10" # 最多 10 个 Service
persistentvolumeclaims: "20"
---
# LimitRange:给命名空间里的容器设置默认值和单容器上下限
apiVersion: v1
kind: LimitRange
metadata:
name: team-limits
namespace: team-a
spec:
limits:
- type: Container
default: # 容器没写 limits 时自动补上的值
cpu: 500m
memory: 256Mi
defaultRequest: # 容器没写 requests 时自动补上的值
cpu: 100m
memory: 128Mi
max: # 单个容器允许的上限
cpu: "2"
memory: 2Gi
min: # 单个容器允许的下限
cpu: 50m
memory: 64Mi
使用要点:
- 命名空间配了
requests.cpu这类配额后,所有 Pod 都必须显式声明 requests/limits,否则创建被拒——所以 Quota 和 LimitRange 通常一起上,靠 LimitRange 补默认值。 - LimitRange 的 default 是「补默认值」而非强制覆盖:Pod 自己写了就用自己的。
- 按团队划分命名空间并配好这两个资源,是小型多租户集群的轻量方案。
NetworkPolicy
适用场景:默认集群内所有 Pod 互通,用 NetworkPolicy 做网络层隔离。推荐姿势是先默认拒绝全部流量,再按需放行。需要网络插件支持(Calico、Cilium 等)。
# 第一步:默认拒绝该命名空间的所有入站和出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: team-a
spec:
podSelector: {} # 空选择器 = 选中命名空间内所有 Pod
policyTypes:
- Ingress # 入站
- Egress # 出站
# 不写 ingress/egress 规则 = 对应方向全部拒绝
---
# 第二步:按需放行——允许同命名空间内带 app=gateway 标签的 Pod 访问 my-app 的 80 端口
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-gateway-to-my-app
namespace: team-a
spec:
podSelector: # 规则作用的目标 Pod
matchLabels:
app: my-app
policyTypes:
- Ingress
ingress:
- from:
- podSelector: # 来源:同命名空间内带这些标签的 Pod
matchLabels:
app: gateway
ports:
- protocol: TCP
port: 80 # 只放行 80 端口
---
# 出站也要放行:允许 my-app 访问集群 DNS
# (默认拒绝后域名解析会失败,这是最容易踩的坑)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-my-app-dns
namespace: team-a
spec:
podSelector:
matchLabels:
app: my-app
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: # 目标:kube-system 命名空间(CoreDNS 所在)
matchLabels:
kubernetes.io/metadata.name: kube-system
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
使用要点:
- NetworkPolicy 是白名单叠加模型:多条策略的放行规则取并集,只要有一条放行就通。
- 默认拒绝后最常忘记放行 DNS,症状是 Pod 里域名解析全部超时,排查半天才发现是网络策略。
- 策略只解决「谁能连谁」,不加密流量;要加密得靠服务网格的 mTLS。